# encoding: utf-8
"""
编译器：负责将语句编译为解释器可理解的指令
"""
from .grammar import grammar
from .token import token
from .check import check
from .optimize import optimize

from share import ver, types, english, nums, what_type
from loader.codes_loader import f_codes
from TVM.types.string import string
from loader.func_loader import func_num
from Error.Error import compile_error


# 与名称相对应的指令，无参数，固定-1
codes_list: dict = {
    "+": "ADD",
    "-": "SUB",
    "*": "MUL",
    "/": "DIV",
    "**": "POW",
    "%": "MOD",
    "//": "ZDIV",
    "import": "IMPORT",
    "del": "DEL",
    ">": "GREATER",
    "<": "LESS",
    "==": "EQUAL",
    "!=": "UNEQUAL",
    ">=": "GREATER_EQUAL",
    "<=": "LESS_EQUAL",
    "if": "JUMP_IF_FALSE",
    "while": "JUMP_IF_FALSE",
    "#": "NOP",
    "}": "NOP",
    "": "NOP",
    "goto": "GOTO",
    "bool": "CHANGE_TYPE",
    "assert": "ASSERT",
}

# 与名称相对应的指令，有参数
codes_argv = {
    ":=": "STORE_NAME",
    "=": "CHANGE_VALUE",
}


def add_number(num):
    """把值添加到参数列表中，避免浪费空间
    @param num: 任何需要添加进入常量池的数据类型
    """
    if num not in values:
        values.append(num)


def add_name(name: str):
    """把名字添加到名字列表中，避免浪费空间
    @param name: 任何需要添加进入命名列表的名字
    """
    if name not in names:
        names.append(name)


def add_func(func: str):
    """把函数名添加到名字列表中，为了判断函数类型
    @param func: 任何需要添加进入临时函数命名列表的名字（不保存到ctree文件里，仅在编译期间生成）
    """
    if func not in funcs:
        funcs.append(func)


strip = lambda code: code.strip(" ").strip("\t")
start = lambda codes: [strip(i) for i in codes.split("\n")]


def Compiler(codes) -> tuple:
    """被调用的编译器，不做编译工作，仅做一些初始化工作：token和grammar的生成在此处进行
    @param codes: 接受字符串输入，并进行一系列处理
    @return: 由元组组成的ctree文件（内存中）
    """
    global values, names, classes, funcs
    # 将列表转化二维列表，为token
    exetoken = token(start(codes))
    # 做语法检查，如果不过关会直接终止编译的过程
    check(exetoken)
    # 生成语法树
    exegrammar = grammar(exetoken)
    values = []
    names = []
    funcs = []
    classes = {}
    # 每条语句都生成指令并放入列表
    codes_ = [[(f_codes[j[0]], j[1]) for j in __compile(i)] for i in exegrammar]
    # 优化字节码
    return optimize((ver, codes_, values, names, classes))


def __compile(trees: list):
    """编译器"""
    # 返回列表
    return_list = []
    # 取指
    for tree in trees:
        if isinstance(tree, list):
            # 如果为列表：语法树
            return_list += __compile(tree)
        elif isinstance(tree, str):
            # 如果为字符串：指令名称
            try:
                if tree == "func":
                    add_func(tree)
                    return_list.append(["FUNC", -1])
                else:
                    return_list.append((codes_list[tree], -1))
            except KeyError:
                # 非指令名称且为字符串：函数或注释等
                if tree in funcs:
                    # 自定义函数
                    add_number(tree)
                    return_list.append(["LOAD_VALUE", values.index(tree)])
                    return_list.append(["RUN_FUNC", -1])
                else:
                    pass
        elif isinstance(tree, tuple):
            data_add = tree[0]
            if tree[1] == "data":
                if ('"' in data_add[0]) or ("'" in data_add[0]):
                    # 字符串
                    # 做数值转化
                    data_add = what_type(data_add)[0]
                    add_number(data_add)
                    return_list.append(("LOAD_VALUE", values.index(data_add)))
                elif data_add[0] in nums:
                    # 整数或常数
                    data_add = what_type(data_add)[0]
                    add_number(data_add)
                    return_list.append(("LOAD_VALUE", values.index(data_add)))
                elif data_add[0] in english:
                    # 变量
                    add_name(data_add)
                    return_list.append(("LOAD_NAME", names.index(data_add)))
            elif tree[1] == "builtin_func":
                func_num_name = func_num[data_add]
                add_number(func_num_name)
                return_list.append(("LOAD_VALUE", values.index(func_num_name)))
                return_list.append(("BUILTIN_FUNC", -1))
            elif data_add in codes_argv:
                data_temp = what_type(tree[1][0])[0]
                add_name(data_temp)
                return_list.append((codes_argv[data_add], names.index(data_temp)))
    return return_list
